package com.penta.core;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.dialect.Props;
import cn.hutool.setting.dialect.PropsUtil;
import com.penta.config.ConfigLoader;
import com.penta.util.ConfigUtil;
import com.penta.util.ExcelConverterUtil;
import com.penta.util.FileUtil;
import com.penta.util.JsonResult;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.AncestorListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.*;
import java.awt.peer.SystemTrayPeer;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URL;

/**
 * Excel转换的窗口系统
 */
public class App {

    /**
     * 1、创建一个窗口对象
     */
    public JFrame jFrame;

    /**
     * 定义一个系统托盘
     */
    public SystemTray systemTray;

    /**
     * 定义托盘
     */
    public TrayIcon trayIcon;

    /**
     * 定义一个
     */

    public String title = "ExcelConverterApp";


    /**
     * 定义一个导出目录，用于存放生成文件的存放位置
     */
    public String exportPath;

    /**
     * 用来保存上次选择的目录
     */
    private File lastSelectedDirectory;

    /**
     * 模板类型
     *      （1）模板1
     *      （2）模板2
     */
    private String moduleType = "1";    // 默认为模板1


    /**
     * 为了避免同时打开两个该应用
     * 定义一个 ServerSocket
     */
    private static final int LOCK_PORT = 12345;   // 限定一个端口

    private ConfigLoader configLoader;

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(LOCK_PORT)) {
            new App();

            serverSocket.accept();     // 空等连接，主要目的就是只允许运行一个
        } catch (Exception e) {
            JOptionPane.showMessageDialog(null,"ExcelConverterApp已经被另一个程序运行");
        }



    }

    /**
     * 构造函数
     */
    public App() {

        // 加载配置
        configLoader = new ConfigLoader("config.properties");

        /**
         * 初始化配置信息
         */
        // exportPath = ConfigUtil.get("exportPath");
        exportPath = configLoader.getProperty("exportPath");

        /**
         * 初始化一个窗体
         */
        initJFrame(title);    // 初始化

        // 加入到系统托盘
        addSystemTray();

        createComponent();

        // 显示窗体
        this.jFrame.setVisible(true);

    }

    /**
     * 创建组件：弹簧布局
     * 核心原理：
     *      就是先定位好一个组件的位置，其他的组件根据这个组件来定义相对的位置
     */
    private void createComponent() {

        /**
         * 定义一个弹簧布局 ： SpringLayout
         */
        SpringLayout springLayout = new SpringLayout();

        /**
         * 定义一个 Jpanel, 并让该 Jpanel 的布局方式为弹簧布局
         */
        JPanel jPanel = new JPanel(springLayout);

        /**
         * 组件一：定义一个文件选择器的标题与文件选择器组件
         */
        JLabel fileSelectLabel = new JLabel("请选择原始Excel文件：");
        jPanel.add(fileSelectLabel);
        // 设置组件布局
        execComponentLayout(springLayout,fileSelectLabel,50,80);

        /**
         * 组件二：定义一个文件选择显示的文本框，用于显示选择文件后，显示文件情况
         */
        JTextField fileShowField = new JTextField();
        fileShowField.setPreferredSize(new Dimension(300,20));    // 设置宽度为300，高度为20
        jPanel.add(fileShowField);
        // 设置组件布局，相对于第一个组件的布局
        execComponentLayout(springLayout,fileShowField,SpringLayout.WEST,0,fileSelectLabel);
        execComponentLayout(springLayout,fileShowField,SpringLayout.NORTH,0,fileSelectLabel);

        /**
         * 组件三：定义一个按钮，用于点击后选择文件
         */
        JButton fileSelectButton = new JButton("选择");
        jPanel.add(fileSelectButton);
        // 设置组件布局，相对于第二个组件
        execComponentLayout(springLayout,fileSelectButton,SpringLayout.WEST,10,fileShowField);
        execComponentLayout(springLayout,fileSelectButton,SpringLayout.NORTH,0,fileShowField);
        // 设置选择文件按钮的事件
        fileSelectButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();     // 触发一个文件选择
                if(lastSelectedDirectory !=null && lastSelectedDirectory.exists()) {
                    fileChooser.setCurrentDirectory(lastSelectedDirectory);
                }

                // 限制只能选择 excel 格式的文件
                fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Excel Files","xlsx","xls"));
                int returnValue = fileChooser.showOpenDialog(jFrame);     // 选择文件后返回
                if(returnValue == JFileChooser.APPROVE_OPTION) {
                    File selectedFile = fileChooser.getSelectedFile();

                    /**
                     * 重新设置上次选择文件的目录
                     */
                    lastSelectedDirectory = selectedFile.getParentFile();

                    String fileName = selectedFile.getName();
                    // 检查后缀名
                    if(!fileName.toLowerCase().endsWith(".xlsx") && !fileName.toLowerCase().endsWith(".xls")) {
                        fileShowField.setText("");      // 清空已经选择的内容
                        //JOptionPane.showConfirmDialog(jFrame,"请选择.xlsx格式的文件！","错误",JOptionPane.CANCEL_OPTION);
                        JOptionPane.showMessageDialog(jFrame,"请选择.xlsx 或 xls 格式的文件");
                    } else {
                        fileShowField.setText(selectedFile.getAbsolutePath());
                    }

                }
            }
        });

        /**
         * 组件四：定义一个 checkGroup 组件
         */
       /* JLabel moduleSelectLabel = new JLabel("选择模板类型：");
        jPanel.add(moduleSelectLabel);
        // 设置组件布局
        execComponentLayout(springLayout,moduleSelectLabel,80,120);

        JButton module1Button = new JButton("模板1");
        module1Button.setBackground(Color.BLUE);          // 默认蓝色表示选中
        module1Button.setForeground(Color.WHITE);
        JButton module2Button = new JButton("模板2");
        module2Button.setBackground(Color.GRAY);         // 默认灰色，表示没有选中
        module2Button.setForeground(Color.WHITE);
        jPanel.add(module1Button);
        jPanel.add(module2Button);

        // 设置模板1、模板2的位置
        execComponentLayout(springLayout,module1Button,SpringLayout.WEST,20,moduleSelectLabel);
        execComponentLayout(springLayout,module1Button,SpringLayout.NORTH,0,moduleSelectLabel);

        execComponentLayout(springLayout,module2Button,SpringLayout.WEST,20,module1Button);
        execComponentLayout(springLayout,module2Button,SpringLayout.NORTH,0,module1Button);

        // 设置模板1的事件
        module1Button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moduleType="1";
                module1Button.setBackground(Color.BLUE);          // 默认蓝色表示选中
                module1Button.setForeground(Color.WHITE);

                module2Button.setBackground(Color.GRAY);          // 默认蓝色表示选中
                module2Button.setForeground(Color.WHITE);
            }
        });
        // 设置模板2的事件
        module2Button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                moduleType="2";
                module2Button.setBackground(Color.BLUE);          // 默认蓝色表示选中
                module2Button.setForeground(Color.WHITE);

                module1Button.setBackground(Color.GRAY);          // 默认蓝色表示选中
                module1Button.setForeground(Color.WHITE);
            }
        });*/

        /**
         * 组件五：定义一个按钮，开始转换
         */
        JButton startConverterButton = new JButton("开始转换");
        startConverterButton.setPreferredSize(new Dimension(400,30));
        jPanel.add(startConverterButton);
        // 设置组件布局
        execComponentLayout(springLayout,startConverterButton,80,160);
        // 设置转换按钮的事件
        startConverterButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String selectedFilePath = fileShowField.getText();    // 获取文件所在的路径
                if(StrUtil.isBlank(selectedFilePath)) {
                    JOptionPane.showMessageDialog(jFrame,"请选择文件后，再执行转换操作！");
                } else {
                    /**
                     * 增加正在处理的滚动条
                     */
                    JDialog handlingDialog = new JDialog(jFrame,"正在转换中",false);
                    handlingDialog.setSize(new Dimension(400,600));
                    handlingDialog.setLocationRelativeTo(jFrame);     // 居中显示

                    JPanel panel = new JPanel(new BorderLayout());
                    JProgressBar progressBar = new JProgressBar(0, 100);
                    progressBar.setIndeterminate(true); // 不确定模式，即进度条持续移动

                    panel.add(progressBar, BorderLayout.CENTER);

                    handlingDialog.add(panel);

                    /**
                     * 增加滚动条结束
                     */
                    handlingDialog.setVisible(true);    // 显示滚动条

                    System.out.println("准备调用执行转换");
                    ExcelConverterUtil excelConverterUtil = new ExcelConverterUtil(selectedFilePath,exportPath,moduleType);
                    JsonResult jsonResult = excelConverterUtil.execConverter();
                    System.out.println("返回处理结果");
                    try {
                        Thread.sleep(1000);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    //System.out.println("休息了一秒，准备关闭弹窗");
                    handlingDialog.dispose();     // 关闭正在处理
                    fileShowField.setText(null);     // 将文件清空
                    // System.out.println("关闭结束");
                    if(jsonResult.getCode()==200) {
                        // 表示生成成功
                        JOptionPane.showMessageDialog(jFrame,"转换成功");
                    } else {
                        // 表示生成失败
                        JOptionPane.showMessageDialog(jFrame,"转换失败,原因：" + jsonResult.getMessage());
                    }
                }
            }
        });


        /**
         * 组件六：读取配置文件，显示输出的目录
         */
        JLabel exportPathLabel = new JLabel("输出结果目录：" + exportPath);
        jPanel.add(exportPathLabel);
        // 设置布局信息
        execComponentLayout(springLayout,exportPathLabel,50,250);


        /**
         * 组件七：打开输出目录
         */
        JButton openExportPathButton = new JButton("打开");
        jPanel.add(openExportPathButton);

        // 设置进入按钮的目录布局信息
        execComponentLayout(springLayout,openExportPathButton,200,280);
        // 设置进入按钮的点击事件
        openExportPathButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(Desktop.isDesktopSupported()) {
                    try {

                        /**
                         * 获取输出目录，如果不存在，则直接打开该目录
                         */
                        File directory = FileUtil.getPathAndMkdirs(exportPath);

                        Desktop.getDesktop().open(directory);

                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(jFrame,"无法打开，可能目录不存在");
                    }
                } else {
                    JOptionPane.showMessageDialog(jFrame,"对不起，当前系统暂不支持直接打开目录");
                }
            }
        });


        /**
         * 组件八： 更换输出目录
         */
        JButton changeExportPathButton = new JButton("重选输出目录");
        jPanel.add(changeExportPathButton);
        // 更改输出目录按钮的布局
        execComponentLayout(springLayout,changeExportPathButton,SpringLayout.WEST,20,openExportPathButton);
        execComponentLayout(springLayout,changeExportPathButton,SpringLayout.NORTH,0,openExportPathButton);
        // 重选输出目录按钮事件
        changeExportPathButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                fileChooser.setDialogTitle("请重新选择输出结果目录");

                int returnValue = fileChooser.showOpenDialog(jFrame);     // 选择文件后返回
                if(returnValue == JFileChooser.APPROVE_OPTION) {
                    File selectedPath = fileChooser.getSelectedFile();
                    exportPath = selectedPath.getAbsolutePath();        // 输出全路径
                    exportPathLabel.setText("输出结果目录：" + exportPath);

                    // 并写回配置文件中
                    //ConfigUtil.set("exportPath",exportPath);              // 写回
                    configLoader.setProperty("exportPath",exportPath);      // 写回
                }

            }
        });

        /**
         * 增加退出按钮
         */
        /**
         * 组件八： 更换输出目录
         */
        JButton exitButton = new JButton("退出");
        jPanel.add(exitButton);
        // 更改输出目录按钮的布局
        execComponentLayout(springLayout,exitButton,SpringLayout.WEST,20,changeExportPathButton);
        execComponentLayout(springLayout,exitButton,SpringLayout.NORTH,0,changeExportPathButton);
        // 重选输出目录按钮事件
        exitButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);     // 马上退出
            }
        });


        /**
         * 取出内容容器
         */
        Container contentPane = jFrame.getContentPane();
        contentPane.add(jPanel);

    }

    /**
     * 对组件进行布局
     *
        这里涉及到三个重要的概念
        （1） SpringLayout: 布局管理器
        （2） SpringLayout.Constraints: 使用弹簧布局的容器里面的组件的布局约束，每个组件对应一个
        （3） Spring: 可以理解为：一个能够进行四则运算的整数

     *
     * @param springLayout
     *              布局管理器
     * @param component
     *              具体的组件
     * @param x
     *          x的偏移值
     * @param y
     *          y的偏移值
     */
    private void execComponentLayout(SpringLayout springLayout,JComponent component,int x,int y) {

        /**
         * 取出当前组件的布局约束
         */
        SpringLayout.Constraints constraints = springLayout.getConstraints(component);

        constraints.setX(Spring.constant(x));
        constraints.setY(Spring.constant(y));

    }

    /**
     * 对组件进行相对布局
     *
     * @param springLayout
     *              布局管理器
     * @param component
     *              当前组件
     * @param direction
     *              方向
     * @param offset
     *              偏移量
     * @param refer
     *              相对组件
     */
    private void execComponentLayout(SpringLayout springLayout,JComponent component,String direction,int offset,JComponent refer) {

        /**
         * 取出当前组件的约束器
         */
        SpringLayout.Constraints constraints = springLayout.getConstraints(component);

        /**
         * 取出参照组件的约束器
         */
        SpringLayout.Constraints referConstraints = springLayout.getConstraints(refer);
        // 先取出参照组件的相同方向的偏移情况
        Spring referComponentOffSet = null;
        if(direction.equals(SpringLayout.WEST)) {
            referComponentOffSet = referConstraints.getConstraint(SpringLayout.EAST);
        } else if(direction.equals(SpringLayout.EAST)) {
            referComponentOffSet = referConstraints.getConstraint(SpringLayout.WEST);
        } else {
            referComponentOffSet = referConstraints.getConstraint(direction);
        }

        /**
         * 设置当前组件的指定方向的偏移量
         */
        constraints.setConstraint(direction,Spring.sum(referComponentOffSet,Spring.constant(offset)));

    }


    /**
     * 初始化窗体
     * @param title
     */
    private void initJFrame(String title) {

        /**
         * 1、创建一个窗口对象
         */
        jFrame = new JFrame(title);

        // 设置窗口的关闭操作（EXIT_ON_CLOSE表示关闭窗口时退出程序）
        jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        /**
         * 设置应用的图标
         */
        URL logo = App.class.getClassLoader().getResource("pentaLogo.png");
        Image image = new ImageIcon(logo).getImage();
        jFrame.setIconImage(image);

        /**
         * 2、窗口的大小、位置,单位：像素
         */
        jFrame.setSize(600,400);    // 300 * 500
        jFrame.setLocationRelativeTo(null);      // 居中显示

    }

    /**
     * 初始化系统托盘的内容
     */
    private void addSystemTray() {
        /**
         * 是否支持系统托盘
         */
        if(SystemTray.isSupported()) {

            /**
             * 实例化系统托盘
             */
            systemTray = SystemTray.getSystemTray();

            /**
             * 实例托盘，并设置托盘的图标图片
             */
            URL logo = App.class.getClassLoader().getResource("pentaLogo.png");
            Image image = new ImageIcon(logo).getImage();
            trayIcon = new TrayIcon(image);
            trayIcon.setImageAutoSize(true);
            try {
                /**
                 * 将托盘加入到系统托盘
                 */
                systemTray.add(trayIcon);
            } catch (Exception e) {
                e.printStackTrace();
            }

            /**
             * 增加最小化时，销毁资源
             */
            jFrame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowIconified(WindowEvent e) {
                    jFrame.dispose();
                }
            });

            /**
             * 托盘事件监听
             */
            trayIcon.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    int clickCount = e.getClickCount();     // 鼠标是否有点击
                    if(clickCount == 1) {
                        jFrame.setExtendedState(JFrame.NORMAL);
                    }
                    jFrame.setVisible(true);
                }
            });

        }

    }


}
